from django.shortcuts import render, redirect
from django.views import View
from django import http
import re, json
from django.contrib.auth import login, authenticate, logout
from django_redis import get_redis_connection
from django.db.models import Q
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from meiduo_mall.utils.views import LoginRequiredView
from django.db import DatabaseError

from .models import User, Address
from meiduo_mall.utils.response_code import RETCODE
from celery_tasks.email.tasks import send_verify_email_url
from .utils import generate_email_verify_url, check_email_user
import logging
from goods.models import SKU
from carts.utils import merge_cart_cookie_to_redis
logger = logging.getLogger('django')


class RegisterView(View):
    """注册"""

    def get(self, request):
        '''展示注册界面'''
        return render(request, 'register.html')

    def post(self, request):
        '''注册逻辑'''
        # 1. 接收前端表单数据
        query_dict = request.POST
        username = query_dict.get('username')
        password = query_dict.get('password')
        password2 = query_dict.get('password2')
        mobile = query_dict.get('mobile')
        sms_code_client = query_dict.get('sms_code')
        allow = query_dict.get('allow')  # 复选框没有指定标签valuen属性值,那么如果勾选 'on',未勾选 None

        # 2. 前端数据拿到后,在使用之前都要做校验
        # all()  遍历列表中每一个元素 判断是否为None '', [], () .. 只要其中有一个是空 直接返回False
        if all([username, password, password2, mobile, sms_code_client, allow]) is False:
            return http.HttpResponseForbidden('缺少必传参数')

        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return http.HttpResponseForbidden('请输入5-20个字符的用户名')

        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.HttpResponseForbidden('请输入8-20个字符的密码')
        if password != password2:
            return http.HttpResponseForbidden('两次输入的密码不一致')

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('请输入正确格式的手机号')

        #  短信验证码校验逻辑,后期补充
        # 2.1 创建redis连接对象
        redis_cli = get_redis_connection('verify_codes')
        # 2.2 获取当前手机号在redis存储的短信验证码
        sms_code_server_bytes = redis_cli.get('sms_%s' % mobile)
        # 2.3 判断短信验证码是否已过期
        if sms_code_server_bytes is None:
            return render(request, 'register.html', {'sms_code_error': '短信验证码已过期'})
        # 2.4 将bytes类型转换为str
        sms_code_server = sms_code_server_bytes.decode()
        # 2.5 判断用户填写的短信验证码是否正确
        if sms_code_client != sms_code_server:
            return render(request, 'register.html', {'sms_code_error': '短信验证码填写错误'})

        # 3. 新增User
        user = User.objects.create_user(username=username, password=password, mobile=mobile)
        # 3.1 注册成功即代表用户登录(状态保持)
        login(request, user)

        # 4. 响应
        # return http.HttpResponse('注册成功了,跳转首页')
        # return redirect('/')  # 重定向到首页
        response = redirect('/')
        # 用户登录成功向cookie中存储username
        response.set_cookie('username', user.username, max_age=settings.SESSION_COOKIE_AGE)
        return response


class UsernameCountView(View):
    """判断用户名是否重复注册"""

    def get(self, request, username):
        # 查询用户名是否重复
        count = User.objects.filter(username=username).count()
        # 响应
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count})  # HTTP: 200


class MobileCountView(View):
    """判断手机号是否重复注册"""

    def get(self, request, mobile):
        # 查询手机号是否重复
        count = User.objects.filter(mobile=mobile).count()
        # 响应
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count})  # HTTP: 200


class LoginView(View):
    """用户登录"""

    def get(self, request):
        """展示用户登录界面"""
        return render(request, 'login.html')

    def post(self, request):
        """只能用户名登录逻辑"""
        # 接收请求体表单数据
        query_dict = request.POST
        username = query_dict.get('username')
        password = query_dict.get('password')
        remembered = query_dict.get('remembered')  # 记住登录,非必勾项  勾选时'on' 未勾选时 None
        # 校验
        if all([username, password]) is False:
            return http.HttpResponseForbidden('缺少必传参数')

        # 认证用户
        # try:
        #     user = User.objects.get(username=username)
        #     if user.check_password(password) is False:
        #         return render(request, 'login.html', {'account_errmsg': '用户名或密码不正确'})
        # except User.DoesNotExist:
        #     return render(request, 'login.html', {'account_errmsg': '用户名或密码不正确'})
        # 用户认证,通过认证返回当前user模型否则返回None
        user = authenticate(request, username=username, password=password)
        if user is None:
            return render(request, 'login.html', {'account_errmsg': '用户名或密码不正确'})

        # 状态保持
        # 如果用户记住登录,状态保持两周
        # 如果用户不勾选记住登录,状态保持到会话结束(关闭浏览器)
        login(request, user)  # 默认两周

        if remembered is None:  # 没有勾选  session 设置为0 和cookie的None都代表关闭浏览器就删除
            request.session.set_expiry(0)  # 将session过期时间设置为关闭浏览器删除

        # 重定向
        # return http.HttpResponse('登录成功去到首页')
        # response = redirect('/')  # 重定向到首页
        response = redirect(request.GET.get('next') or '/')  # 有来源重定向到来源,没有就去首页
        # 调用合并购物车函数
        merge_cart_cookie_to_redis(request, response)
        # if remembered is None:
        #     response.set_cookie('username', user.username, max_age=None)  # cookie过期时间指定为None代表会话结束
        # else:
        #     response.set_cookie('username', user.username, max_age=settings.SESSION_COOKIE_AGE)  # cookie过期时间指定为None代表会话结束
        # 登录成功向用户浏览器cookie中存储username
        response.set_cookie('username',
                            user.username,
                            max_age=None if (
                                    remembered is None) else settings.SESSION_COOKIE_AGE)  # cookie过期时间指定为None代表会话结束
        # 三目: 条件返回值 if 条件 else 不成立时返回值
        return response

    # def post(self, request):
    #     """用户名或手机号登录逻辑"""
    #     # 接收请求体表单数据
    #     query_dict = request.POST
    #     username = query_dict.get('username')
    #     password = query_dict.get('password')
    #     remembered = query_dict.get('remembered')  # 记住登录,非必勾项  勾选时'on' 未勾选时 None
    #     # 校验
    #     if all([username, password]) is False:
    #         return http.HttpResponseForbidden('缺少必传参数')
    #
    #     # 认证用户
    #     # try:
    #     #     user = User.objects.get(Q(username=username) | Q(mobile=username))
    #     #     if user.check_password(password) is False:
    #     #         return render(request, 'login.html', {'account_errmsg': '用户名或密码不正确'})
    #     # except User.DoesNotExist:
    #     #     return render(request, 'login.html', {'account_errmsg': '用户名或密码不正确'})
    #
    #     # 状态保持
    #     # 如果用户记住登录,状态保持两周
    #     # 如果用户不勾选记住登录,状态保持到会话结束(关闭浏览器)
    #     try:
    #         user = User.objects.get(username=username)
    #     except User.DoesNotExist:
    #         try:
    #             user = User.objects.get(mobile=username)
    #         except User.DoesNotExist:
    #             return render(request, 'login.html', {'account_errmsg': '账号或密码不正确'})
    #
    #     if user.check_password(password) is False:
    #         return render(request, 'login.html', {'account_errmsg': '账号或密码不正确'})
    #
    #
    #
    #     login(request, user)  # 默认两周
    #     if remembered is None:  # 没有勾选  session 设置为0 和cookie的None都代表关闭浏览器就删除
    #         request.session.set_expiry(0)  # 将session过期时间设置为关闭浏览器删除
    #
    #     # 重定向
    #     return http.HttpResponse('登录成功去到首页')


class LogoutView(View):
    """退出登录"""

    def get(self, request):
        # 1. 清除状态保持
        logout(request)
        # 2. 清除cookie中的username
        response = redirect('/login/')
        response.delete_cookie('username')
        # 3. 重定向到login界面
        return response


class InfoView(LoginRequiredMixin, View):
    """用户中心"""

    # def get(self, request):
    #     # 判断当前请求用户是否登录
    #     # isinstance(对象, 类名)  # 判断对象是否有 指定的类或子类创建出来的对象
    #     user = request.user
    #     # if isinstance(request.user, User):
    #     if user.is_authenticated:
    #         # 如果是登录用户展示用户中心界面
    #         return render(request, 'user_center_info.html')
    #     else:
    #         # 如果是未登录用户就重定向到login
    #         # return redirect('/login/')
    #         return redirect('/login/?next=/info/')

    def get(self, request):
        return render(request, 'user_center_info.html')


class EmailView(LoginRequiredView):
    """设置用户邮箱"""

    def put(self, request):

        # 获取本次请求的user
        user = request.user
        if not user.email:
            # 1. 接收请求体json
            json_str_bytes = request.body
            json_str = json_str_bytes.decode()
            data = json.loads(json_str)
            email = data.get('email')
            # 2. 校验
            if email is None:
                return http.HttpResponseForbidden('缺少必传参数')
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('请输入正确的邮箱')

            # 3. 修改当前user的email
            user.email = email
            user.save()

        # 在些立即发送激活邮件
        # from django.core.mail import send_mail
        # send_mail(subject='邮箱主题', message='邮件普通', from_email='发件人', recipient_list='收件人列表',
        #          html_message='邮件超文本内容')
        # send_mail(subject='标题',
        #           message='',
        #           from_email='美多商城<itcast99@163.com>',
        #           recipient_list=[user.email], html_message='<a href="http://www.baidu.com">百度一下</a>')
        # 邮箱激活url
        # verify_url = 'http://www.meiduo.com:8000/verify_email/?token="用户唯一信息加密"'
        verify_url = generate_email_verify_url(user)
        # 使用celery进行异步发送邮件
        send_verify_email_url.delay(user.email, verify_url)

        # 响应
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})


class EmailVerifyView(View):
    """激活邮箱"""

    def get(self, request):
        # 1.接收查询参数
        token = request.GET.get('token')
        # 2.校验
        if token is None:
            return http.HttpResponseForbidden('缺少必传参数')
        # 2.1 对token进行解密并返回user
        user = check_email_user(token)
        if user is None:
            return http.HttpResponseForbidden('邮件激活失败')
        # 3.修改用户email_active字段
        user.email_active = True
        user.save()
        # 4.响应
        # return render(request, 'user_center_info.html')
        return redirect('/info/')


class AddressView(LoginRequiredView):
    """展示收货地址"""

    def get(self, request):
        user = request.user
        # 查询当前用户所有未被逻辑删除的收货地址
        address_qs = Address.objects.filter(user=user, is_deleted=False)
        # user.addresses.filter(is_deleted=False)
        # qs转列表, qs中模型转字典
        # 定义一个列表用来包装所有收货地址字典
        address_list = []
        for address in address_qs:
            address_list.append({
                'id': address.id,
                'title': address.title,
                'receiver': address.receiver,
                'province_id': address.province_id,
                'province': address.province.name,
                'city_id': address.city_id,
                'city': address.city.name,
                'district_id': address.district_id,
                'district': address.district.name,
                'place': address.place,
                'mobile': address.mobile,
                'tel': address.tel,
                'email': address.email
            })

        context = {
            'addresses': address_list,  # 当前用户所有收货地址
            'default_address_id': user.default_address_id  # 用户默认收货地址id
        }
        return render(request, 'user_center_site.html', context)


class AddressCreateView(LoginRequiredView):
    """新增收货地址"""

    def post(self, request):
        # alt + 左右光标 单个单词跳转
        # ctrl + 左右光标 跳到行首或行尾
        # shift  选中

        user = request.user
        # 判断用户收货地址数量
        count = Address.objects.filter(user=user, is_deleted=False).count()
        if count == 20:
            return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '收货地址超过上限'})
        # 1.接收请求体json数据
        json_dict = json.loads(request.body.decode())

        title = json_dict.get('title')
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 2.校验
        if all([title, receiver, province_id, city_id, district_id, place, mobile]) is False:
            return http.HttpResponseForbidden('缺少必传参数')

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('请输入正确的手机号')

        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')

        # 3.新增

        try:
            address = Address(
                user=user,
                title=title,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
            address.save()
        except DatabaseError as e:
            logger.error(e)
            return http.HttpResponseForbidden('新增收货地址失败')

        # 如果当前用户还没有默认地址,就将当前新增的地址设置为默认地址
        if user.default_address is None:
            user.default_address = address
            user.save()

        # 把新增的收货地址模型对象转换成字典
        address_dict = {
            'id': address.id,
            'title': address.title,
            'receiver': address.receiver,
            'province_id': address.province_id,
            'province': address.province.name,
            'city_id': address.city_id,
            'city': address.city.name,
            'district_id': address.district_id,
            'district': address.district.name,
            'place': address.place,
            'mobile': address.mobile,
            'tel': address.tel,
            'email': address.email
        }
        # 响应
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '新增收货地址成功', 'address': address_dict})


class UserBrowseHistory(View):
    """商品浏览记录"""
    def post(self, request):
        # 判断本次请求,是否为登录用户,如果未登录直接响应
        user = request.user
        if not user.is_authenticated:
            return http.JsonResponse({'code': RETCODE.SESSIONERR, 'errmsg': '用户未登录什么与不做'})

        # 接收请求体json 数据
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        # 校验
        try:
            sku = SKU.objects.get(id=sku_id, is_launched=True)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku_id不存在')

        # 创建redis连接对象
        redis_cli = get_redis_connection('history')
        # 商品浏览记录 list key
        key = 'history_%s' % user.id
        # 先去重
        redis_cli.lrem(key, 0, sku_id)
        # 添加到列表开头
        redis_cli.lpush(key, sku_id)
        # 截取只存储列表前5个元素
        redis_cli.ltrim(key, 0, 4)
        # 响应
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})

    def get(self, request):
        """商品浏览记录展示"""
        user = request.user
        if not user.is_authenticated:
            return http.JsonResponse({'code': RETCODE.SESSIONERR, 'errmsg': '用户未登录什么也不做'})
        # 创建redis连接对象
        redis_cli = get_redis_connection('history')
        # 获取当前登录用户的所有商品浏览数据
        sku_ids = redis_cli.lrange('history_%s' % user.id, 0, -1)
        # 通过sku_id查询sku模型
        # sku_qs = SKU.objects.filter(id__in=sku_ids)  # 一次查询多个时,默认会按照id从小到大排序(原有顺序破坏了)
        sku_list = []  # 用来保存商品sku 字典
        for sku_id in sku_ids:
            sku = SKU.objects.get(id=sku_id)
            # 模型转字典
            sku_list.append({
                'id': sku.id,
                'name': sku.name,
                'price': sku.price,
                'default_image_url': sku.default_image.url
            })

        # 响应
        return http.JsonResponse({'code': RETCODE.OK,'errmsg': 'OK', 'skus': sku_list})
